﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Reflection;

namespace Kokomo.Controls
{
	public class ColorChooser : Control
	{
		static ColorChooser()
		{
			DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorChooser), new FrameworkPropertyMetadata(typeof(ColorChooser)));

			_namedColorItems = GetColorItems(typeof(Colors));
			NamedColorItemsProperty = DependencyProperty.Register("NamedColorItems", typeof(IEnumerable<ColorItem>), typeof(ColorChooser), new UIPropertyMetadata(_namedColorItems));

			_systemColorItems = GetColorItems(typeof(SystemColors));
			SystemColorItemsProperty = DependencyProperty.Register("SystemColorItems", typeof(IEnumerable<ColorItem>), typeof(ColorChooser), new UIPropertyMetadata(_systemColorItems));
		}

		private static ColorItem[] GetColorItems(Type type)
		{
			PropertyInfo[] colorProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Static);
			List<ColorItem> colorItems = new List<ColorItem>(colorProperties.Length);
			foreach (var colorProperty in colorProperties)
			{
				if (colorProperty.PropertyType == typeof(Color))
				{
					Color color = (Color)colorProperty.GetValue(null, null);
					ColorItem item = new ColorItem(color, colorProperty.Name);
					colorItems.Add(item);
				}
			}

			return colorItems.ToArray();
		}

		private static ColorItem[] _namedColorItems;
		private static ColorItem[] _systemColorItems;

		private bool _isColorChanging;
		private bool _isChannelChanging;

		#region Color
		public Color Color
		{
			get { return (Color)GetValue(ColorProperty); }
			set { SetValue(ColorProperty, value); }
		}

		// Using a DependencyProperty as the backing store for Color.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ColorProperty =
			DependencyProperty.Register("Color", typeof(Color), typeof(ColorChooser), new FrameworkPropertyMetadata(Colors.Black, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnColorChanged));

		private static void OnColorChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnColorChanged();
		}

		public event EventHandler ColorChanged;
		protected virtual void OnColorChanged()
		{
			try
			{
				this._isColorChanging = true;
				if (!this._isChannelChanging)
				{
					Color color = this.Color;
					this.Opacity = color.A;
					this.RedValue = color.R;
					this.GreenValue = color.G;
					this.BlueValue = color.B;
				}
			}
			finally
			{
				this._isColorChanging = false;
			}

			if (this.ColorChanged != null) this.ColorChanged(this, EventArgs.Empty);
		}
		#endregion
		#region NamedColorItems


		public IEnumerable<ColorItem> NamedColorItems
		{
			get { return (IEnumerable<ColorItem>)GetValue(NamedColorItemsProperty); }
			set { SetValue(NamedColorItemsProperty, value); }
		}

		// Using a DependencyProperty as the backing store for NamedColorItems.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty NamedColorItemsProperty;
		#endregion
		#region SystemColorItems


		public IEnumerable<ColorItem> SystemColorItems
		{
			get { return (IEnumerable<ColorItem>)GetValue(SystemColorItemsProperty); }
			set { SetValue(SystemColorItemsProperty, value); }
		}

		// Using a DependencyProperty as the backing store for SystemColorItems.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SystemColorItemsProperty;
		#endregion
		#region SelectedColorItem


		public ColorItem SelectedColorItem
		{
			get { return (ColorItem)GetValue(SelectedColorItemProperty); }
			set { SetValue(SelectedColorItemProperty, value); }
		}

		// Using a DependencyProperty as the backing store for SelectedColorItem.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SelectedColorItemProperty =
			DependencyProperty.Register("SelectedColorItem", typeof(ColorItem), typeof(ColorChooser), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedColorItemChanged));

		private static void OnSelectedColorItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnSelectedColorItemChanged();
		}
		protected virtual void OnSelectedColorItemChanged()
		{
			if (this.SelectedColorItem != null)
			{
				this.Color = this.SelectedColorItem.Color;
			}
		}
		#endregion

		protected void UpdateColorFromArgb()
		{
			try
			{
				this._isChannelChanging = true;

				Color color = Color.FromArgb((byte)(this.Opacity), (byte)this.RedValue, (byte)this.GreenValue, (byte)this.BlueValue);
				this.Color = color;
			}
			finally
			{
				this._isChannelChanging = false;
			}
		}

		#region Color components


		public int Opacity
		{
			get { return (int)GetValue(OpacityProperty); }
			set { SetValue(OpacityProperty, value); }
		}

		// Using a DependencyProperty as the backing store for Opacity.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty OpacityProperty =
			DependencyProperty.Register("Opacity", typeof(int), typeof(ColorChooser), new FrameworkPropertyMetadata(255, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnOpacityChanged, CoerceOpacity));

		private static void OnOpacityChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnOpacityChanged();
		}

		public event EventHandler OpacityChanged;
		protected virtual void OnOpacityChanged()
		{
			this.UpdateColorFromArgb();
			if (this.OpacityChanged != null) this.OpacityChanged(this, EventArgs.Empty);
		}

		private static object CoerceOpacity(DependencyObject d, object baseValue)
		{
			return Math.Max(0, Math.Min(255, (int)baseValue));
		}


		private static object CoerceChannel(DependencyObject d, object baseValue)
		{
			return Math.Max(0, Math.Min(255, (int)baseValue));
		}

		public int RedValue
		{
			get { return (int)GetValue(RedValueProperty); }
			set { SetValue(RedValueProperty, value); }
		}

		// Using a DependencyProperty as the backing store for RedValue.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty RedValueProperty =
			DependencyProperty.Register("RedValue", typeof(int), typeof(ColorChooser), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnRedValueChanged, CoerceOpacity));

		private static void OnRedValueChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnRedValueChanged();
		}

		public event EventHandler RedValueChanged;
		protected virtual void OnRedValueChanged()
		{
			this.UpdateColorFromArgb();
			if (this.RedValueChanged != null) this.RedValueChanged(this, EventArgs.Empty);
		}



		public int GreenValue
		{
			get { return (int)GetValue(GreenValueProperty); }
			set { SetValue(GreenValueProperty, value); }
		}

		// Using a DependencyProperty as the backing store for GreenValue.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty GreenValueProperty =
			DependencyProperty.Register("GreenValue", typeof(int), typeof(ColorChooser), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnGreenValueChanged, CoerceChannel));

		private static void OnGreenValueChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnGreenValueChanged();
		}

		public event EventHandler GreenValueChanged;
		protected virtual void OnGreenValueChanged()
		{
			this.UpdateColorFromArgb();
			if (this.GreenValueChanged != null) this.GreenValueChanged(this, EventArgs.Empty);
		}



		public int BlueValue
		{
			get { return (int)GetValue(BlueValueProperty); }
			set { SetValue(BlueValueProperty, value); }
		}

		// Using a DependencyProperty as the backing store for BlueValue.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty BlueValueProperty =
			DependencyProperty.Register("BlueValue", typeof(int), typeof(ColorChooser), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBlueValueChanged, CoerceChannel));

		private static void OnBlueValueChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
		{
			((ColorChooser)target).OnBlueValueChanged();
		}

		public event EventHandler BlueValueChanged;
		protected virtual void OnBlueValueChanged()
		{
			this.UpdateColorFromArgb();
			if (this.BlueValueChanged != null) this.BlueValueChanged(this, EventArgs.Empty);
		}


		#endregion
	}
}
